﻿using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using NFixedPoint.LookupTable;

namespace NFixedPoint
{
    public class FMath
    {

        public static FixedPoint Sqrt(FixedPoint value)
        {
            if (value == 0)
            {
                return 0;
            }
            //最小差异，测试很多次，18之下，结果没有太大变化。
            const double EPSILON = 18;
            bool flag = true;
            FixedPoint sqrt = value;
            while (flag)
            {
                FixedPoint cur = (sqrt + value / sqrt) >> 1;
                if (Math.Abs(cur.raw - sqrt.raw) < EPSILON)
                {
                    flag=false;
                }
                else
                {
                    sqrt = cur;
                }
            }
            return sqrt;
        }

        public static FixedPoint Clamp(FixedPoint fp, FixedPoint v1, FixedPoint v2)
        {
            return fp < v1 ? v1 : (fp > v2 ? v2 : fp);
        }

        public static FixedPoint Floor(FixedPoint fp)
        {
            fp.raw = fp.raw >> FixedPoint.PRECISION << FixedPoint.PRECISION;
            return fp;
        }

        public static FixedPoint Fractions(FixedPoint fp)
        {
            return new FixedPoint(fp.raw & FixedPoint.FMask);
        }

        public static FixedPoint Min(FixedPoint v1, FixedPoint v2)
        {
            return v1.raw < v2.raw ? v1 : v2;
        }
        public static FixedPoint Max(FixedPoint v1, FixedPoint v2)
        {
            return v1.raw > v2.raw ? v1 : v2;
        }
        public static Factor Acos(FixedPoint cos)
        {
            //将余弦值映射到一个范围内
            FixedPoint r = (cos * AcosLookupTable.HalfCount) + AcosLookupTable.HalfCount;
            //限制在  0-count之间，确保在表的有效范围内。
            r = Clamp(r, FixedPoint.zero, AcosLookupTable.Count);
            //根据索引，找到对应的值，并且返回Factor。
            return new Factor(AcosLookupTable.GetAcos(r.AsInt));
        }
        /// <summary>
        /// 计算正切函数
        /// </summary>
        /// <param name="num">弧度表示的角度</param>
        /// <returns></returns>
        public static FixedPoint Tan(FixedPoint num)
        {
            //取模，限制在一个周期内。
            num %= FixedPoint.pi;
            //乘以系数。相当于讲角度转化为tan函数的输入范围。
            num *= FixedPoint.one_div_pi2;
            //查表，拿到值。
            num.raw = TanLookupTable.Tan(num.raw);
            return num;
        }


    }
}
